package zdream.pmfield.stage.director;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import zdream.pmfield.data.foe.MoveCheckPair;
import zdream.pmfield.stage.action.AttackAction;
import zdream.pmfield.stage.action.AttackReport;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.stage.object.Seat;
import zdream.pmfield.stage.object.StageMove;

/**
 * @author Zdream
 * @date 2023-11-13
 * @since 0.0.1
 */
public class AttackContext {
	public static final String TITLE_ACTION = "action";
	public static final String TITLE_DRY_RUN = "dryRun";
	public static final String TITLE_CHECK_ACCURACY = "checkAccuracy";

	public final AttackAction action;
	public final AttackReport report;
	public final float time;
	public final String title;

	public boolean skipValidation;
	public boolean abort;
	public String abortReason;

	/**
	 * 配置: 使用自己设定的攻击 {@link #attack} 和 {@link #effectFixed}
	 */
	public boolean skipCalcAttack;

	/**
	 * routes 是技能释放途径的 seat (含攻击者所在 seat 和被攻击的 seat, 按到达顺序排列),
	 */
	public final List<Seat> routes = new ArrayList<>();

	public byte movePoints, actionPoints;
	/**
	 * 一般蓄力技会用到, 修改本轮花费的时间. 一般情况下是 0.
	 * @see FoeActor#speedCostRate
	 */
	public float speedCostExtraRate = 0;

	/**
	 * 一般而言同 {@link AttackAction#move}.
	 * 但是如果是蓄力技能的话, currentMove 是它的一个子技能 ({@link StageMove#subMoves}).
	 */
	public StageMove currentMove;
	public FoeActor attacker;
	public Roll[] rolls;
	/**
	 * 额外判定的 Roll, createdBy 必不为 null
	 */
	public Roll[] moreRolls = new Roll[0];
	public MoveCheckPair checkPair;

	/**
	 * 技能威力和攻击力, 攻击方判定
	 */
	public int power, attack;

	/**
	 * 攻击方判定的伤害修正
	 */
	public float effectFixed = 1.0f;

	/**
	 * 防御方效果列表
	 */
	public final List<AttackContextItem> items = new ArrayList<>();

	public AttackContextItem mainItem() {
		if (items.isEmpty()) {
			return null;
		}
		return items.get(0);
	}

	private AttackContext(AttackAction action, String title) {
		this.action = Objects.requireNonNull(action);
		this.time = action.foe.stage.timeline.round;
		this.report = new AttackReport(action, time);
		this.title = Objects.requireNonNull(title);
	}

	public static AttackContext create(AttackAction action) {
		AttackContext context = new AttackContext(action, TITLE_ACTION);
		context.currentMove = action.move;
		context.attacker = action.foe;
		return context;
	}

	public static AttackContext createDryRun(AttackAction action, MoveCheckPair pair) {
		final AttackContext context = new AttackContext(action, TITLE_DRY_RUN);
		context.checkPair = Objects.requireNonNull(pair);
		context.currentMove = action.move;
		context.attacker = action.foe;
		context.skipValidation = true;
		return context;
	}

	public static AttackContext createForAccuracyChecking(AttackAction action) {
		final AttackContext context = new AttackContext(action, TITLE_CHECK_ACCURACY);
		context.currentMove = action.move;
		context.attacker = action.foe;
		context.skipValidation = true;
		return context;
	}

	public void appendMoveRoll(Roll roll) {
		Objects.requireNonNull(roll);
		int length = moreRolls.length;
		moreRolls = Arrays.copyOf(moreRolls, length + 1);
		moreRolls[length] = roll;
	}

	public AttackContextItem createItemForFoe(FoeActor targetFoe) {
		Objects.requireNonNull(targetFoe);
		AttackContextItem item = new AttackContextItem(this, targetFoe, targetFoe.seat);
		items.add(item);
		return item;
	}

	public boolean hasAttacker(FoeActor foe) {
		return foe == action.foe;
	}

	public boolean hasDefender(FoeActor foe) {
		return items.stream().anyMatch(item -> item.targetFoe == foe);
	}

	public AttackContextItem findAttackItemForDefender(FoeActor foe) {
		return items.stream().filter(item -> item.targetFoe == foe).findAny().orElse(null);
	}

	public List<AttackContextItem> createItemForSeat(Seat seat) {
		Objects.requireNonNull(seat);
		final List<FoeActor> foes = seat.findFoes();

		List<AttackContextItem> items = new ArrayList<>();
		if (foes.isEmpty()) {
			AttackContextItem item = new AttackContextItem(this, null, seat);
			this.items.add(item);
			items.add(item);
			return items;
		}

		for (FoeActor foe : foes) {
			AttackContextItem item = new AttackContextItem(this, foe, seat);
			this.items.add(item);
			items.add(item);
		}
		return items;
	}

	public boolean isAction() {
		return TITLE_ACTION.equals(title);
	}

	public boolean isDryRun() {
		return TITLE_DRY_RUN.equals(title);
	}

	public boolean isForAccuracyChecking() {
		return TITLE_CHECK_ACCURACY.equals(title);
	}

	public static class AttackContextItem {
		public final AttackContext parent;
		public final FoeActor targetFoe;
		public final Seat targetSeat;
		/**
		 * 防御力, 防御方判定
		 */
		public int defense;

		// 加成
		public float typeFixed = 1f;
		public final List<FixPair> typeFixPairs = new ArrayList<>();
		/**
		 * defenseFixed: 防御方判定的伤害修正, 越高表示防御越高, 伤害越少
		 * powerFixed: 技能伤害修正, 包括技能溅射到的只受一半伤害. 越高则伤害越高
		 */
		public float defenseFixed, powerFixed;

		public boolean miss;
		public int damage;

		private AttackContextItem(AttackContext parent, FoeActor targetFoe, Seat seat) {
			this.parent = Objects.requireNonNull(parent);
			this.targetFoe = targetFoe; // may be null
			this.targetSeat = Objects.requireNonNull(seat);
		}
	}
}
